home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / vfs / vfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  38.3 KB  |  1,649 lines

  1. /* Virtual File System switch code
  2.    Copyright (C) 1995 The Free Software Foundation
  3.    
  4.    Written by: 1995 Miguel de Icaza
  5.                1995 Jakub Jelinek
  6.    
  7.    This program is free software; you can redistribute it and/or modify
  8.    it under the terms of the GNU General Public License as published by
  9.    the Free Software Foundation; either version 2 of the License, or
  10.    (at your option) any later version.
  11.    
  12.    This program is distributed in the hope that it will be useful,
  13.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.    GNU General Public License for more details.
  16.  
  17.    You should have received a copy of the GNU General Public License
  18.    along with this program; if not, write to the Free Software
  19.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  20.  
  21. #include <config.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>    /* For atol() */
  24. #include <stdarg.h>
  25. #include <string.h>
  26. #include <errno.h>
  27. #include <sys/types.h>
  28. #include <malloc.h>
  29. #include <fcntl.h>
  30. #include <signal.h>
  31. #include <time.h>
  32. #include <sys/time.h>
  33. #include "../src/fs.h"
  34. #include "../src/mad.h"
  35. #include "../src/dir.h"
  36. #include "../src/util.h"
  37. #include "../src/main.h"
  38. #include "../src/panel.h"
  39. #include "../src/key.h"        /* Required for the async alarm handler */
  40. #include "vfs.h"
  41. #include "mcfs.h"
  42. #include "names.h"
  43. #include "extfs.h"
  44. #ifdef USE_NETCODE
  45. #   include "tcputil.h"
  46. #endif
  47.  
  48. extern int get_other_type (void);
  49.  
  50. int vfs_timeout = 60; /* VFS timeout in seconds */
  51.  
  52. extern int cd_symlinks; /* Defined in main.c */
  53.  
  54. /* They keep track of the current directory */
  55. static vfs *current_vfs = &local_vfs_ops;
  56. char *current_dir = NULL;
  57. char *last_current_dir = NULL;
  58.  
  59. static int current_mon;
  60. static int current_year;
  61.  
  62. /* Open files managed by the vfs layer */
  63. #define MAX_VFS_FILES 100
  64. static struct {
  65.     void *fs_info;
  66.     vfs  *operations;
  67. } vfs_file_table [MAX_VFS_FILES];
  68.  
  69. static int get_bucket ()
  70. {
  71.     int i;
  72.  
  73.     /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
  74.     for (i = 3; i < MAX_VFS_FILES; i++){
  75.     if (!vfs_file_table [i].fs_info)
  76.         return i;
  77.     }
  78.     fprintf (stderr, "No more virtual file handles\n");
  79.     exit (1);
  80. }
  81.  
  82. /* This flag is set on each vfs_type call to say if the path was
  83.    absolute (1) or relative (0) to current dir. */
  84.  
  85. int vfs_type_absolute = 0;
  86.  
  87. vfs *vfs_type (char *path)
  88. {
  89.     vfs *vfs;
  90.  
  91.     vfs_type_absolute = 0;
  92.     vfs = current_vfs;
  93.     
  94.     if (*path == '/') {
  95.         vfs = &local_vfs_ops;
  96.         vfs_type_absolute = 1;
  97.     }
  98.     
  99.     if (strncmp (path, "local:", 6) == 0){
  100.     vfs = &local_vfs_ops;
  101.     vfs_type_absolute = 1;
  102.     }
  103.  
  104. #ifdef USE_NETCODE
  105.     if (strncmp (path, "mc:", 3) == 0){
  106.     vfs = &mcfs_vfs_ops;
  107.     vfs_type_absolute = 1;
  108.     }
  109.     
  110.     if (strncmp (path, "ftp://", 6) == 0){
  111.         vfs = &ftpfs_vfs_ops;
  112.         vfs_type_absolute = 1;
  113.     }
  114. #endif
  115.  
  116. #ifdef USE_EXT2FSLIB
  117.     if (strncmp (path, "undel:", 6) == 0){
  118.     vfs = &undelfs_vfs_ops;
  119.     vfs_type_absolute = 1;
  120.     }
  121. #endif
  122.     if (strncmp (path, "tar:", 4) == 0){
  123.         vfs = &tarfs_vfs_ops;
  124.     vfs_type_absolute = 1;
  125.     }
  126.     
  127.     if (extfs_prefix_to_type (path) != -1) {
  128.         vfs = &extfs_vfs_ops;
  129.         vfs_type_absolute = 1;
  130.     }
  131.     return vfs;
  132. }
  133.  
  134. char *vfs_path (char *path)
  135. {
  136.     int j;
  137.  
  138.     if (strncmp (path, "local:", 6) == 0)
  139.     return path + 6;
  140.  
  141. #ifdef USE_NETCODE
  142.     if (strncmp (path, "mc:", 3) == 0)
  143.     return strchr(path + 3, '/');
  144.     
  145.     if (strncmp (path, "ftp://", 6) == 0)
  146.     return strchr(path + 6, '/');
  147. #endif
  148.  
  149. #ifdef USE_EXT2FSLIB
  150.     if (strncmp (path, "undel:", 6) == 0)
  151.     return path + 6;
  152. #endif
  153.     if (strncmp (path, "tar:", 4) == 0)
  154.     return path + 4;
  155.  
  156.     j = extfs_prefix_to_type (path);
  157.     if (j != -1)
  158.         return path + strlen (extfs_get_prefix (j)) + 1;
  159.         
  160.     return path;
  161. }
  162.  
  163. static struct vfs_stamping *stamps;
  164.  
  165. /* Returns the number of seconds remaining to the vfs timeout
  166.  *
  167.  * FIXME: currently this is set to 10 seconds.  We should compute this.
  168.  */
  169. int vfs_timeouts ()
  170. {
  171.     return stamps ? 10 : 0;
  172. }
  173.  
  174. void vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent)
  175. {
  176.     if (v != &local_vfs_ops && id != (vfsid)-1) {
  177.         struct vfs_stamping *stamp;
  178.         
  179.         for (stamp = stamps; stamp != NULL; stamp = stamp->next)
  180.             if (stamp->v == v && stamp->id == id) {
  181.         gettimeofday(&(stamp->time), NULL);
  182.                 return;
  183.         }
  184.         stamp = xmalloc (sizeof (struct vfs_stamping), "vfs stamping");
  185.         stamp->v = v;
  186.         stamp->id = id;
  187.         stamp->parent = parent;
  188.         gettimeofday (&(stamp->time), NULL);
  189.         stamp->next = stamps;
  190.         stamps = stamp;
  191.     }
  192. }
  193.  
  194. void vfs_stamp (vfs *v, vfsid id)
  195. {
  196.     struct vfs_stamping *stamp;
  197.     
  198.     for (stamp = stamps; stamp != NULL; stamp = stamp->next)
  199.         if (stamp->v == v && stamp->id == id) {
  200.             gettimeofday (&(stamp->time), NULL);
  201.             if (stamp->parent != NULL)
  202.                 vfs_stamp (stamp->parent->v, stamp->parent->id);
  203.             return;
  204.         }
  205. }
  206.  
  207. void vfs_rmstamp (vfs *v, vfsid id, int removeparents)
  208. {
  209.     struct vfs_stamping *stamp, *st1, *st2, *st3;
  210.     
  211.     for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
  212.         if (stamp->v == v && stamp->id == id) {
  213.             if (stamp->parent != NULL) {
  214.                 if (removeparents)
  215.                     vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
  216.                 for (st2 = stamp->parent, st3 = st2->next; st3 != NULL; st2 = st3, st3 = st3->next)
  217.                     free (st2);
  218.                 free (st2);
  219.             }
  220.             if (st1 == NULL) {
  221.                 stamps = stamp->next;
  222.             } else {
  223.                 st1->next = stamp->next;
  224.             }
  225.             free (stamp);
  226.             return;
  227.         }
  228. }
  229.  
  230. int mc_open (char *file, int flags, ...)
  231. {
  232.     int  handle;
  233.     int  mode;
  234.     void *info;
  235.     vfs  *vfs;
  236.     va_list ap;
  237.     
  238.     file = vfs_canon (file);
  239.     vfs = vfs_type (file);
  240.  
  241.     /* Get the mode flag */
  242.     va_start (ap, flags);
  243.     mode = va_arg (ap, int);
  244.     va_end (ap);
  245.     
  246.     info = (*vfs->open) (file, flags, mode);
  247.     if (!info){
  248.     errno = (*vfs->ferrno)();
  249.     free (file);
  250.     return -1;
  251.     }
  252.     handle = get_bucket ();
  253.     vfs_file_table [handle].fs_info = info;
  254.     vfs_file_table [handle].operations = vfs;
  255.     
  256.     free (file);
  257.     return handle;
  258. }
  259.  
  260. #define vfs_op(handle) vfs_file_table [handle].operations 
  261. #define vfs_info(handle) vfs_file_table [handle].fs_info
  262. #define vfs_free_bucket(handle) vfs_info(handle) = 0;
  263.  
  264. int mc_read (int handle, char *buffer, int count)
  265. {
  266.     vfs *vfs;
  267.     int result;
  268.  
  269.     if (handle == -1)
  270.     return -1;
  271.         
  272.     vfs = vfs_op (handle);
  273.     result = (*vfs->read)(vfs_info (handle), buffer, count);
  274.     if (result == -1){
  275.     errno = (*vfs->ferrno)();
  276.     return -1;
  277.     }
  278.     return result;
  279. }
  280.  
  281. int mc_ctl (int handle, int ctlop, int arg)
  282. {
  283.     vfs *vfs;
  284.     int result;
  285.  
  286.     vfs = vfs_op (handle);
  287.     if (vfs->ctl == NULL)
  288.         return 0;
  289.     result = (*vfs->ctl)(vfs_info (handle), ctlop, arg);
  290.     return result;
  291. }
  292.  
  293. int mc_setctl (char *path, int ctlop, char *arg)
  294. {
  295.     vfs *vfs;
  296.     int result;
  297.  
  298.     path = vfs_canon (path);
  299.     vfs = vfs_type (path);    
  300.     if (vfs->setctl == NULL) {
  301.         free (path);
  302.         return 0;
  303.     }
  304.     result = (*vfs->setctl)(path, ctlop, arg);
  305.     free (path);
  306.     return result;
  307. }
  308.  
  309. int mc_close (int handle)
  310. {
  311.     vfs *vfs;
  312.     int result;
  313.  
  314.     if (handle == -1)
  315.     return -1;
  316.     
  317.     vfs = vfs_op (handle);
  318.     if (handle < 3)
  319.     return close (handle);
  320.  
  321.     result = (*vfs->close)(vfs_info (handle));
  322.     vfs_free_bucket (handle);
  323.     
  324.     return result;
  325. }
  326.  
  327. DIR *mc_opendir (char *dirname)
  328. {
  329.     int  handle;
  330.     void *info;
  331.     vfs  *vfs;
  332.     char *p = NULL;
  333.     int i = strlen (dirname);
  334.  
  335.     if (dirname [i - 1] != '/') { 
  336.     /* We should make possible reading of the root directory in a tar file */
  337.         p = xmalloc (i + 2, "slash");
  338.         strcpy (p, dirname);
  339.         strcpy (p + i, "/");
  340.         dirname = p;
  341.     }
  342.     dirname = vfs_canon (dirname);
  343.     vfs = vfs_type (dirname);
  344.  
  345.     info = (*vfs->opendir)(dirname);
  346.     if (!info){
  347.     errno = (*vfs->ferrno)();
  348.     free (dirname);
  349.     if (p)
  350.         free (p);
  351.     return NULL;
  352.     }
  353.     handle = get_bucket ();
  354.     vfs_file_table [handle].fs_info = info;
  355.     vfs_file_table [handle].operations = vfs;
  356.  
  357.     free (dirname);
  358.     if (p)
  359.         free (p);
  360.     return (DIR *) handle;
  361. }
  362.  
  363. /* This should strip the non needed part of a path name */
  364. #define vfs_name(x) x
  365.  
  366. struct dirent *mc_readdir(DIR *dirp)
  367. {
  368.     int handle;
  369.     vfs *vfs;
  370.  
  371.     if (!dirp){
  372. #ifdef EBADF
  373.     errno = EBADF;
  374. #else
  375.     errno = 1;
  376. #endif
  377.     return NULL;
  378.     }
  379.     handle = (int) dirp;
  380.     vfs = vfs_op (handle);
  381.     return (*vfs->readdir)(vfs_info (handle));
  382. }
  383.  
  384. int mc_closedir (DIR *dirp)
  385. {
  386.     int handle = (int) dirp;
  387.     vfs *vfs = vfs_op (handle);
  388.     int result;
  389.  
  390.     result = (*vfs->closedir)(vfs_info (handle));
  391.     vfs_free_bucket (handle);
  392.     return result; 
  393. }
  394.  
  395. int mc_stat (char *path, struct stat *buf)
  396. {
  397.     vfs *vfs;
  398.     int result;
  399.  
  400.     path = vfs_canon (path);
  401.     vfs = vfs_type (path);    
  402.     result = (*vfs->stat)(vfs_name (path), buf);
  403.     free (path);
  404.     if (result == -1){
  405.     errno = (*vfs->ferrno)();
  406.     return -1;
  407.     }
  408.     return result;
  409. }
  410.  
  411. int mc_lstat (char *path, struct stat *buf)
  412. {
  413.     vfs *vfs;
  414.     int result;
  415.  
  416.     path = vfs_canon (path);
  417.     vfs = vfs_type (path);    
  418.     result = (*vfs->lstat)(vfs_name (path), buf);
  419.     free (path);
  420.     if (result == -1){
  421.     errno = (*vfs->ferrno)();
  422.     return -1;
  423.     }
  424.     return result;
  425. }
  426.  
  427. int mc_fstat (int handle, struct stat *buf)
  428. {
  429.     vfs *vfs;
  430.     int result;
  431.  
  432.     if (handle == -1)
  433.     return -1;
  434.     
  435.     vfs = vfs_op (handle);
  436.     result = (*vfs->fstat)(vfs_info (handle), buf);
  437.     if (result == -1){
  438.     errno = (*vfs->ferrno)();
  439.     return -1;
  440.     }
  441.     return result;
  442. }
  443.  
  444. /* FIXME: We should return the buffer, not set it, since it could
  445.    overflow */
  446. char *mc_get_current_wd (char *buffer, int size)
  447. {
  448.     char *p;
  449.     struct stat my_stat, my_stat2;
  450.     
  451.     if (current_vfs == &local_vfs_ops){
  452. #ifdef HAVE_GETWD
  453.     p = (char *) getwd (buffer);
  454. #else
  455.     /* FIXME: this code is just a quick hack */
  456.     p = getcwd (buffer, size);
  457. #endif
  458.     if (!p) { /* One of the directories in the path is not readable */
  459.         strcpy (buffer, current_dir);
  460.         return buffer;
  461.     }
  462.  
  463.     /* Otherwise check if it is O.K. to use the current_dir */
  464.     mc_stat (buffer, &my_stat);
  465.     mc_stat (current_dir, &my_stat2);
  466.     if (my_stat.st_ino != my_stat2.st_ino ||
  467.         my_stat.st_dev != my_stat2.st_dev ||
  468.         !cd_symlinks) {
  469.         free (current_dir);
  470.         current_dir = strdup (p);
  471.         last_current_dir = current_dir;
  472.         return p;
  473.     } /* Otherwise we return current_dir below */
  474.     } 
  475.     strcpy (buffer, current_dir);
  476.     return buffer;
  477. }
  478.  
  479. int mc_chmod (char *path, int mode)
  480. {
  481.     vfs *vfs;
  482.     int result;
  483.     
  484.     path = vfs_canon (path);
  485.     vfs = vfs_type (path);    
  486.     result = (*vfs->chmod)(vfs_name (path), mode);
  487.     free (path);
  488.     if (result == -1){
  489.     errno = (*vfs->ferrno)();
  490.     return -1;
  491.     }
  492.     return result;
  493. }
  494.  
  495. int mc_chown (char *path, int owner, int group)
  496. {
  497.     vfs *vfs;
  498.     int result;
  499.     
  500.     path = vfs_canon (path);
  501.     vfs = vfs_type (path);    
  502.     result = (*vfs->chown)(vfs_name (path), owner, group);
  503.     free (path);
  504.     if (result == -1){
  505.     errno = (*vfs->ferrno)();
  506.     return -1;
  507.     }
  508.     return result;
  509. }
  510.  
  511. int mc_readlink(char *path, char *buf, int bufsiz)
  512. {
  513.     vfs *vfs;
  514.     int result;
  515.     
  516.     path = vfs_canon (path);
  517.     vfs = vfs_type (path);    
  518.     result = (*vfs->readlink)(vfs_name (path), buf, bufsiz);
  519.     free (path);
  520.     if (result == -1){
  521.     errno = (*vfs->ferrno)();
  522.     return -1;
  523.     }
  524.     return result;
  525. }
  526.  
  527. int mc_unlink (char *path)
  528. {
  529.     vfs *vfs;
  530.     int result;
  531.     
  532.     path = vfs_canon (path);
  533.     vfs = vfs_type (path);    
  534.     result = (*vfs->unlink)(vfs_name (path));
  535.     free (path);
  536.     if (result == -1){
  537.     errno = (*vfs->ferrno)();
  538.     return -1;
  539.     }
  540.     return result;
  541. }
  542.  
  543. int mc_symlink (char *name1, char *name2)
  544. {
  545.     vfs *vfs2 = 0;
  546.     int result;
  547.  
  548.     name2 = vfs_canon (name2);
  549.     vfs2 = vfs_type (name2);    
  550.     
  551.     result = (*vfs2->symlink)(vfs_name (name1), vfs_name (name2));
  552.     free (name2);
  553.     if (result == -1){
  554.     errno = (*vfs2->ferrno)();
  555.     return -1;
  556.     }
  557.     return result;
  558. }
  559.  
  560. int mc_link (char *name1, char *name2)
  561. {
  562.     vfs *vfs1;
  563.     vfs *vfs2;
  564.     int result;
  565.  
  566.     name1 = vfs_canon (name1);
  567.     vfs1 = vfs_type (name1);    
  568.     name2 = vfs_canon (name2);
  569.     vfs2 = vfs_type (name2);    
  570.     
  571.     if (vfs1 != vfs2){
  572.         errno = EXDEV;
  573.         free (name1);
  574.         free (name2);
  575.     return -1;
  576.     }
  577.  
  578.     result = (*vfs1->link)(vfs_name (name1), vfs_name (name2));
  579.     free (name1);
  580.     free (name2);
  581.     if (result == -1){
  582.     errno = (*vfs1->ferrno)();
  583.     return -1;
  584.     }
  585.     return result;
  586. }
  587.  
  588. int mc_write (int fd, char *buf, int nbyte)
  589. {
  590.     vfs *vfs; 
  591.     int result;
  592.  
  593.     if (fd == -1)
  594.     return -1;
  595.     
  596.     vfs = vfs_op (fd);
  597.     result = (*vfs->write)(vfs_info (fd), buf, nbyte);
  598.     if (result == -1){
  599.     errno = (*vfs->ferrno)();
  600.     return -1;
  601.     }
  602.     return result;
  603. }
  604.  
  605. int mc_rename (char *path1, char *path2)
  606. {
  607.     vfs *vfs1;
  608.     vfs *vfs2;
  609.     int result;    
  610.  
  611.     path1 = vfs_canon (path1);
  612.     vfs1 = vfs_type (path1);    
  613.     path2 = vfs_canon (path2);
  614.     vfs2 = vfs_type (path2);    
  615.     
  616.     if (vfs1 != vfs2){
  617.     errno = EXDEV;
  618.     free (path1);
  619.     free (path2);
  620.     return -1;
  621.     }
  622.  
  623.     result = (*vfs1->rename)(vfs_name (path1), vfs_name (path2));
  624.     free (path1);
  625.     free (path2);
  626.     if (result == -1){
  627.     errno = (*vfs1->ferrno)();
  628.     return -1;
  629.     }
  630.     return result;
  631. }
  632.  
  633. off_t mc_lseek (int fd, off_t offset, int whence)
  634. {
  635.     vfs *vfs;
  636.  
  637.     if (fd == -1)
  638.     return -1;
  639.  
  640.     vfs = vfs_op (fd);
  641.     return (*vfs->lseek)(vfs_info (fd), offset, whence);
  642. }
  643.  
  644. int is_special_prefix (char *path)
  645. {
  646.     if (strncmp (path, "mc:", 3) == 0)
  647.     return 1;
  648.     if (strncmp (path, "local:", 6) == 0)
  649.     return 1;
  650.     if (strncmp (path, "tar:", 4) == 0)
  651.     return 1;
  652.     if (extfs_prefix_to_type (path) != -1)
  653.         return 1;
  654.     if (strncmp (path, "ftp://", 6) == 0)
  655.         return 1;
  656.     if (strncmp (path, "undel:", 6) == 0)
  657.     return 1;
  658.     return 0;
  659. }
  660.  
  661. char *vfs_canon (char *path)
  662. {
  663.     vfs *vfs;
  664.  
  665.     vfs = current_vfs;
  666.     
  667.     if (strncmp (path, "local:", 6) == 0) {
  668.     path += 7;
  669.     vfs = &local_vfs_ops;
  670. #ifdef USE_NETCODE
  671.     } else if (strncmp (path, "mc:", 3) == 0) {
  672.     vfs = &mcfs_vfs_ops;
  673.     } else if (strncmp (path, "ftp://", 6) == 0) {
  674.         vfs = &ftpfs_vfs_ops;
  675. #endif
  676. #ifdef USE_EXT2FSLIB
  677.     } else if (strncmp (path, "undel:", 6) == 0){
  678.     vfs = &undelfs_vfs_ops;
  679. #endif
  680.     } else if (strncmp (path, "tar:", 4) == 0) {
  681.         vfs = &tarfs_vfs_ops;
  682.     } else if (extfs_prefix_to_type (path) != -1) {
  683.         vfs = &extfs_vfs_ops;
  684.     } else if (*path == '/') { /* Absolute local */
  685.         vfs = &local_vfs_ops;
  686.     } else if (*path == '~') { /* Tilde expansion */
  687.         char *local, *result;
  688.  
  689.         local = tilde_expand (path);
  690.     if (local){
  691.         result = vfs_canon (local);
  692.         free (local);
  693.         return result;
  694.     } else 
  695.         return strdup (path);
  696.     } else { /* Relative to current directory */
  697.         char *local, *result;
  698.  
  699.     if (current_dir [strlen (current_dir) - 1] == '/')
  700.         local = copy_strings (current_dir, path, NULL);
  701.     else
  702.         local = copy_strings (current_dir, "/", path, NULL);
  703.  
  704.     result = vfs_canon (local);
  705.     free (local);
  706.     return result;
  707.     }
  708.     
  709.     if (vfs == &local_vfs_ops)
  710. /* NOTE: No local:/ is necessary at the moment, we are handling absolute paths
  711.          at the moment, even it is impossible, since it doesn't work then at all. */    
  712.     return strdup (canonicalize_pathname (path));
  713. #ifdef USE_EXT2FSLIB
  714.     if (vfs == &undelfs_vfs_ops){
  715.     return strdup (path);
  716.     }
  717. #endif
  718. #ifdef USE_NETCODE
  719.     if (vfs == &ftpfs_vfs_ops || vfs == &mcfs_vfs_ops) {
  720.     char *q, *p, *r, *s;
  721.     int prefixsh, isftp;
  722.     
  723.     if (vfs == &ftpfs_vfs_ops) {
  724.         prefixsh = 6;
  725.         isftp = 1;
  726.     } else {
  727.         prefixsh = 3;
  728.         isftp = 0;
  729.     }
  730.     
  731.     q = strdup (path);
  732.     p = strchr (q + prefixsh, '/');
  733.     
  734.     if (p != NULL) {
  735.         if (p [0] && p [1] == '~') { /* Tilde expansion */
  736.             *p = 0;
  737.             if (isftp) {
  738.                 s = ftpfs_gethome (q);
  739.                 if (s == NULL)
  740.                     s = "/";
  741.             } else {
  742.                 s = mcfs_gethome (q);
  743.                 if (s == NULL)
  744.                     s = strdup ("/");
  745.             }
  746.             if (p [2] == '/')
  747.                 p += 3;
  748.             else
  749.                 p += 2;
  750.             r = copy_strings (q, s, p, NULL);
  751.             if (!isftp)
  752.                 free (s);
  753.             free (q);
  754.             q = r;
  755.             p = strchr (q + prefixsh, '/');
  756.         }
  757.         p++;
  758.         if (p [0])
  759.         canonicalize_pathname (p);
  760.         else
  761.         p = ".";
  762.         
  763.         if (!strncmp (p, "..", 2) && (p [2] == '/' || !p [2])) {
  764.             *p = 0;
  765.             r = isftp ? ftpfs_getupdir (q) : mcfs_getupdir (q);
  766.             if (r == NULL)
  767.                 r = "/";
  768.             p += 2;
  769.             if (*p == '/')
  770.                 p++;
  771.             r = copy_strings (r, p, NULL);
  772.             free (q);
  773.             q = vfs_canon (r);
  774.             free (r);
  775.         }
  776.     }
  777.     return q;
  778.     }
  779. #endif
  780.     if (vfs == &tarfs_vfs_ops || vfs == &extfs_vfs_ops) {
  781.         char *local_path, *arc_name, *p, *q, *result, *extfs_prefix = NULL;
  782.         int istar, extfs_type;
  783.         
  784.         /* There has to be a leading tar: or extfs prefix in the path */
  785.  
  786.         istar = (vfs == &tarfs_vfs_ops);
  787.         if (istar)
  788.             local_path = tarfs_analysis (path, &arc_name, 0);
  789.         else {
  790.             local_path = extfs_analysis (path, &arc_name, &extfs_type, 0);
  791.             extfs_prefix = extfs_get_prefix (extfs_type);
  792.         }
  793.         if (local_path == NULL) {
  794.             return strdup ("/"); /* For error cases cd to the root dir */
  795.         }
  796.         p = (*local_path == '/') ? local_path + 1 : local_path;
  797.         if (*p)
  798.             canonicalize_pathname (p);
  799.         if (!strcmp (p, ".")) /* This is an incorrect result of canonicalization,
  800.                                  if we have p = somedir/.. */
  801.         *p = 0;                                 
  802.         if (p [0] != '.' || p [1] != '.' || (p [2] && p [2] != '/')) { 
  803.             /* I.e. p doesn't start with the .. directory */
  804.             if (istar)
  805.             result = copy_strings ("tar:", arc_name, "/", p, 0);
  806.         else {
  807.         /* Two chances: the extfs requires an archive to
  808.          * use or it does not need it:
  809.          */
  810.  
  811.         /* If it does need an archive... */
  812.         if (arc_name && *arc_name)
  813.             result = copy_strings
  814.             (extfs_prefix, ":", arc_name, "/", p, 0);
  815.         else
  816.             result = strdup (path);
  817.         }
  818.         free (local_path);
  819.         free (arc_name);
  820.         return result;
  821.     } else {
  822.         /* We want to move outside of the archive */
  823.         if (!istar && (!arc_name || !*arc_name)) {
  824.             free (local_path);
  825.             if (arc_name) free (arc_name);
  826.             return strdup ("/");
  827.         }
  828.         q = strrchr (arc_name, '/');
  829.         if (q == NULL)
  830.             q = arc_name; /* Should not happen */
  831.         else
  832.             q++; 
  833.         *q = 0;
  834.         if (p [2] == '/')
  835.             p += 3;
  836.         else
  837.             p += 2;
  838.         result = copy_strings (arc_name, p, 0);
  839.         free (local_path);
  840.         free (arc_name);
  841.         local_path = vfs_canon (result);
  842.         free (result);
  843.         return local_path;
  844.     }
  845.     }
  846.  
  847.     fprintf (stderr, "Could not happend\n");
  848.     return 0;
  849. }
  850.  
  851. static vfsid vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
  852. {
  853.     vfsid nvfsid;
  854.     int freeit = 0;
  855.  
  856.     if (dir [strlen (dir) - 1] != '/') {
  857.         dir = copy_strings (dir, "/", NULL);    
  858.         freeit = 1;
  859.     }
  860.     nvfsid = (*nvfs->getid)(dir, par);
  861.     if (freeit)
  862.         free (dir);
  863.     return nvfsid;
  864. }
  865.  
  866. void vfs_add_noncurrent_stamps (vfs *oldvfs, vfsid oldvfsid, struct vfs_stamping *parent)
  867. {
  868.     vfs *nvfs, *n2vfs;
  869.     vfsid nvfsid, n2vfsid;
  870.     struct vfs_stamping *par;
  871.  
  872.         /* FIXME: As soon as we convert to multiple panels, this stuff
  873.            has to change. It works like this: We do not time out the
  874.            vfs's which are current in any panel and on the other
  875.            side we add the old directory with all its parents which
  876.            are not in any panel (if we find such one, we stop adding
  877.            parents to the time-outing structure. */
  878.  
  879.     if (!cpanel)
  880.     return;
  881.     
  882.     nvfs = vfs_type (current_dir);
  883.     nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
  884.     vfs_rmstamp (nvfs, nvfsid, 1);
  885.     if (nvfs != oldvfs || nvfsid != oldvfsid) {
  886.         struct vfs_stamping *stamp;
  887.  
  888.     if (get_other_type () == view_listing){
  889.         n2vfs = vfs_type (opanel->cwd);
  890.             n2vfsid = vfs_ncs_getid (n2vfs, opanel->cwd, &par);
  891.     } else {
  892.         n2vfs = (vfs *) -1;
  893.         n2vfsid = (vfs *) -1;
  894.     }
  895.     
  896.         if ((n2vfs != oldvfs || n2vfsid != oldvfsid) &&
  897.             oldvfsid != (vfsid)-1 && 
  898.             (*oldvfs->nothingisopen) (oldvfsid)) {
  899.             vfs_addstamp (oldvfs, oldvfsid, parent);
  900.             for (stamp = parent; stamp != NULL; stamp = stamp->parent) {
  901.                 if ((stamp->v == nvfs && stamp->id == nvfsid) ||
  902.                     (stamp->v == n2vfs && stamp->id == n2vfsid) ||
  903.                     stamp->id == (vfsid)-1 ||
  904.                     !(*stamp->v->nothingisopen) (stamp->id))
  905.                     break;
  906.                 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
  907.             }
  908.         }
  909.     }
  910. }
  911.  
  912. static void vfs_stamp_path (char *path)
  913. {
  914.     vfs *vfs;
  915.     vfsid id;
  916.     struct vfs_stamping *par;
  917.     
  918.     vfs = vfs_type (path);
  919.     id  = vfs_ncs_getid (vfs, path, &par);
  920.     vfs_addstamp (vfs, id, par ? par->parent : par);
  921. }
  922.  
  923. void vfs_add_current_stamps (void)
  924. {
  925.     vfs_stamp_path (cpanel->cwd);
  926.     if (get_other_type () == view_listing){
  927.     WPanel *p = (WPanel *) get_panel_widget (get_other_index ());
  928.     vfs_stamp_path (p->cwd);
  929.     }
  930. }
  931.  
  932. /* This function is really broken */
  933. int mc_chdir (char *path)
  934. {
  935.     char *a;
  936.     int result;
  937.     char *p = NULL;
  938.     int i = strlen (path);
  939.     vfs *oldvfs;
  940.     vfsid oldvfsid;
  941.     struct vfs_stamping *parent;
  942.  
  943.     if (path [i - 1] != '/') {
  944.     /* We should make possible reading of the root directory in a tar archive */
  945.         p = xmalloc (i + 2, "slash");
  946.         strcpy (p, path);
  947.         strcpy (p + i, "/");
  948.         path = p;
  949.     }
  950.     
  951.     if (!current_dir) {
  952.     current_dir = strdup ("/"); /* Note: we should set current_dir
  953.                                    on startup to a reasonable pwd.
  954.                                    It cannot be relative, i.e. no
  955.                                    . or anything else except
  956.                                    /, tar:, mcfs:, local:, ftp:, 
  957.                                    $extfs_prefix .
  958.                                    This is done by the vfs_setup_wd call
  959.                                    in main.c */
  960.     last_current_dir = current_dir;
  961.     }                                   
  962.  
  963.     oldvfs = vfs_type (current_dir);
  964.     oldvfsid = vfs_ncs_getid (oldvfs, current_dir, &parent);
  965.     
  966.     a = current_dir; /* Save a copy for case of failure */
  967.     last_current_dir = a;
  968.     current_dir = vfs_canon (path);
  969.     current_vfs = vfs_type (current_dir);
  970.     result = (*current_vfs->chdir)(vfs_name (current_dir));
  971.     if (result == -1) {
  972.     errno = (*current_vfs->ferrno)();
  973.         free (current_dir);
  974.         current_vfs = vfs_type (a);
  975.         current_dir = a;
  976.     } else {
  977.         free (a);
  978.         last_current_dir = current_dir;
  979.         vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
  980.     }
  981.     if (p) {
  982.         free (p);
  983.     }
  984.     if (*current_dir) {
  985.     p = strchr (current_dir, 0) - 1;
  986.     if (*p == '/' && p > current_dir)
  987.         *p = 0; /* Sometimes we assume no trailing slash on cwd */
  988.     }
  989.     return result;
  990. }
  991.  
  992. int vfs_current_is_local (void)
  993. {
  994.     return current_vfs == &local_vfs_ops;
  995. }
  996.  
  997. int vfs_current_is_extfs (void)
  998. {
  999.     return current_vfs == &extfs_vfs_ops;
  1000. }
  1001.  
  1002. int vfs_file_is_local (char *filename)
  1003. {
  1004.     vfs *vfs;
  1005.     
  1006.     filename = vfs_canon (filename);
  1007.     vfs = vfs_type (filename);
  1008.     free (filename);
  1009.     return vfs == &local_vfs_ops;
  1010. }
  1011.  
  1012. int vfs_file_is_ftp (char *filename)
  1013. {
  1014. #ifdef USE_NETCODE
  1015.     vfs *vfs;
  1016.     
  1017.     filename = vfs_canon (filename);
  1018.     vfs = vfs_type (filename);
  1019.     free (filename);
  1020.     return vfs == &ftpfs_vfs_ops;
  1021. #else
  1022.     return 0;
  1023. #endif
  1024. }
  1025.  
  1026. char *vfs_get_current_dir (void)
  1027. {
  1028.     return current_dir;
  1029. }
  1030.  
  1031. static void vfs_setup_wd (void)
  1032. {
  1033.     current_dir = xmalloc (MC_MAXPATHLEN, "Current directory");
  1034.     get_current_wd (current_dir, MC_MAXPATHLEN);
  1035.     if (errno != 0)
  1036.     current_dir [0] = 0;
  1037.     current_dir [MC_MAXPATHLEN - 1] = 0;
  1038.     last_current_dir = current_dir;
  1039.     return;
  1040. }
  1041.  
  1042. int mc_mkdir (char *path, mode_t mode)
  1043. {
  1044.     vfs *vfs;
  1045.     int result;
  1046.  
  1047.     path = vfs_canon (path);
  1048.     vfs = vfs_type (path);    
  1049.     result = (*vfs->mkdir)(vfs_name (path), mode);
  1050.     free (path);
  1051.     if (result == -1){
  1052.     errno = (*vfs->ferrno)();
  1053.     return -1;
  1054.     }
  1055.     return result;
  1056. }
  1057.  
  1058. int mc_rmdir (char *path)
  1059. {
  1060.     vfs *vfs;
  1061.     int result;
  1062.  
  1063.     path = vfs_canon (path);
  1064.     vfs = vfs_type (path);    
  1065.     result = (*vfs->rmdir)(vfs_name (path));
  1066.     free (path);
  1067.     if (result == -1){
  1068.     errno = (*vfs->ferrno)();
  1069.     return -1;
  1070.     }
  1071.     return result;
  1072. }
  1073.  
  1074. int mc_mknod (char *path, int mode, int dev)
  1075. {
  1076.     vfs *vfs;
  1077.     int result;
  1078.  
  1079.     path = vfs_canon (path);
  1080.     vfs = vfs_type (path);    
  1081.     result = (*vfs->mknod)(vfs_name (path), mode, dev);
  1082.     free (path);
  1083.     if (result == -1){
  1084.     errno = (*vfs->ferrno)();
  1085.     return -1;
  1086.     }
  1087.     return result;
  1088. }
  1089.  
  1090. #ifdef HAVE_MMAP
  1091. struct mc_mmaping {
  1092.     caddr_t addr;
  1093.     void *vfs_info;
  1094.     vfs *vfs;
  1095.     struct mc_mmaping *next;
  1096. } *mc_mmaparray = NULL;
  1097.  
  1098. caddr_t mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
  1099. {
  1100.     vfs *vfs;
  1101.     caddr_t result;
  1102.     struct mc_mmaping *mcm;
  1103.  
  1104.     if (fd == -1)
  1105.     return (caddr_t) -1;
  1106.     
  1107.     vfs = vfs_op (fd);
  1108.     result = (*vfs->mmap)(addr, len, prot, flags, vfs_info (fd), offset);
  1109.     if (result == (caddr_t)-1){
  1110.     errno = (*vfs->ferrno)();
  1111.     return (caddr_t)-1;
  1112.     }
  1113.     mcm = (struct mc_mmaping *) xmalloc (sizeof (struct mc_mmaping), "vfs: mmap handling");
  1114.     mcm->addr = result;
  1115.     mcm->vfs_info = vfs_info (fd);
  1116.     mcm->vfs = vfs;
  1117.     mcm->next = mc_mmaparray;
  1118.     mc_mmaparray = mcm;
  1119.     return result;
  1120. }
  1121.  
  1122. int mc_munmap (caddr_t addr, size_t len)
  1123. {
  1124.     struct mc_mmaping *mcm, *mcm2 = NULL;
  1125.     
  1126.     for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next) {
  1127.         if (mcm->addr == addr) {
  1128.             if (mcm2 == NULL)
  1129.                 mc_mmaparray = mcm->next;
  1130.             else
  1131.                 mcm2->next = mcm->next;
  1132.             (*mcm->vfs->munmap)(addr, len, mcm->vfs_info);
  1133.             free (mcm);
  1134.             return 0;
  1135.         }
  1136.     }
  1137.     return -1;
  1138. }
  1139.  
  1140. #endif
  1141.  
  1142. char *mc_def_getlocalcopy (char *filename)
  1143. {
  1144.     char *tmp;
  1145.     int fdin, fdout, i;
  1146.     char buffer[8192];
  1147.     struct stat mystat;
  1148.  
  1149.     fdin = mc_open (filename, O_RDONLY);
  1150.     if (fdin == -1)
  1151.         return NULL;
  1152.     tmp = tmpnam(NULL);
  1153.     fdout = creat (tmp, 0600);
  1154.     if (fdout == -1) {
  1155.         mc_close (fdin);
  1156.         return NULL;
  1157.     }
  1158.     tmp = strdup (tmp);
  1159.     while ((i = mc_read (fdin, buffer, sizeof (buffer))) == sizeof (buffer)) {
  1160.         write (fdout, buffer, i);
  1161.     }
  1162.     if (i != -1)
  1163.         write (fdout, buffer, i);
  1164.     mc_close (fdin);
  1165.     close (fdout);
  1166.     if (mc_stat (filename, &mystat) != -1) {
  1167.         chmod (tmp, mystat.st_mode);
  1168.     }
  1169.     return tmp;
  1170. }
  1171.  
  1172. char *mc_getlocalcopy (char *path)
  1173. {
  1174.     vfs *vfs;
  1175.     char *result;
  1176.  
  1177.     path = vfs_canon (path);
  1178.     vfs = vfs_type (path);    
  1179.     result = (*vfs->getlocalcopy)(vfs_name (path));
  1180.     free (path);
  1181.     if (result == NULL){
  1182.     errno = (*vfs->ferrno)();
  1183.     return NULL;
  1184.     }
  1185.     return result;
  1186. }
  1187.  
  1188. void mc_def_ungetlocalcopy (char *filename, char *local, int has_changed)
  1189. {
  1190.     if (has_changed) {
  1191.         int fdin, fdout, i;
  1192.         char buffer[8192];
  1193.     
  1194.         fdin = open (local, O_RDONLY);
  1195.         if (fdin == -1) {
  1196.             unlink (local);
  1197.             free (local);
  1198.             return;
  1199.         }
  1200.         fdout = mc_open (filename, O_WRONLY | O_TRUNC);
  1201.         if (fdout == -1) {
  1202.             close (fdin);
  1203.             unlink (local);
  1204.             free (local);
  1205.             return;
  1206.         }
  1207.         while ((i = read (fdin, buffer, sizeof (buffer))) == sizeof (buffer)) {
  1208.             mc_write (fdout, buffer, i);
  1209.         }
  1210.         if (i != -1)
  1211.             mc_write (fdout, buffer, i);
  1212.         close (fdin);
  1213.         mc_close (fdout);
  1214.     }
  1215.     unlink (local);
  1216.     free (local);
  1217. }
  1218.  
  1219. void mc_ungetlocalcopy (char *path, char *local, int has_changed)
  1220. {
  1221.     vfs *vfs;
  1222.  
  1223.     path = vfs_canon (path);
  1224.     vfs = vfs_type (path);    
  1225.     (*vfs->ungetlocalcopy)(vfs_name (path), local, has_changed);
  1226.     free (path);
  1227. }
  1228.  
  1229. inline int timeoutcmp (struct timeval *t1, struct timeval *t2)
  1230. {
  1231.     return ((t1->tv_sec < t2->tv_sec)
  1232.         || ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
  1233. }
  1234.  
  1235. void vfs_timeout_handler ()
  1236. {
  1237.     struct timeval time;
  1238.     struct vfs_stamping *stamp, *st;
  1239.  
  1240.     gettimeofday (&time, NULL);
  1241.     time.tv_sec -= vfs_timeout;
  1242.     for (stamp = stamps; stamp != NULL;) {
  1243.         if (timeoutcmp (&stamp->time, &time)) {
  1244.             st = stamp->next;
  1245.             (*stamp->v->free) (stamp->id);
  1246.         vfs_rmstamp (stamp->v, stamp->id, 0);
  1247.         stamp = st;
  1248.         } else
  1249.             stamp = stamp->next;
  1250.     }
  1251. }
  1252.  
  1253. void vfs_init (void)
  1254. {
  1255.     time_t current_time;
  1256.     struct tm *t;
  1257.     
  1258.     current_time = time (NULL);
  1259.     t = localtime (¤t_time);
  1260.     current_mon = t->tm_mon;
  1261.     current_year = t->tm_year;
  1262.     
  1263. #ifdef USE_NETCODE
  1264.     tcp_init();
  1265.     ftpfs_init();
  1266. #endif
  1267.     extfs_init ();
  1268.     vfs_setup_wd ();
  1269. }
  1270.  
  1271. void vfs_free_resources (char *path)
  1272. {
  1273.     vfs *vfs;
  1274.     vfsid vid;
  1275.     struct vfs_stamping *parent;
  1276.     
  1277.     vfs = vfs_type (path);
  1278.     vid = vfs_ncs_getid (vfs, path, &parent);
  1279.     if (vid != (vfsid) -1)
  1280.     (*vfs->free)(vid);
  1281. }
  1282.  
  1283. /* Shutdown a vfs given a path name */
  1284. void vfs_shut_path (char *p)
  1285. {
  1286.     vfs *the_vfs;
  1287.     struct vfs_stamping *par;
  1288.  
  1289.     the_vfs = vfs_type (p);
  1290.     vfs_ncs_getid (the_vfs, p, &par);
  1291.     (*par->v->free)(par->id);
  1292. }
  1293.  
  1294. void vfs_shut (void)
  1295. {
  1296.     struct vfs_stamping *stamp;
  1297.  
  1298.     for (stamp = stamps; stamp != NULL; stamp = stamp->next)
  1299.     (*stamp->v->free)(stamp->id);
  1300.  
  1301.     if (current_dir)
  1302.     free (current_dir);
  1303.  
  1304. #ifdef USE_NETCODE
  1305.     ftpfs_done();
  1306. #endif
  1307.  
  1308. }
  1309.  
  1310. /* These ones grab information from the VFS
  1311.  *  and handles them to an upper layer
  1312.  */
  1313. void vfs_fill_names (void (*func)(char *))
  1314. {
  1315. #ifdef USE_NETCODE
  1316.     mcfs_fill_names (func);
  1317.     ftpfs_fill_names (func);
  1318. #endif
  1319.     tarfs_fill_names (func);
  1320.     extfs_fill_names (func);
  1321. }
  1322.  
  1323. /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
  1324. #define MAXCOLS 30
  1325.  
  1326. static char *columns [MAXCOLS];    /* Points to the string in column n */
  1327. static int   column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
  1328. static int  numcols;
  1329.  
  1330. static int split_text (char *p)
  1331. {
  1332.     char *original = p;
  1333.  
  1334.     for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
  1335.     while (*p == ' ' || *p == '\r' || *p == '\n'){
  1336.         *p = 0;
  1337.         p++;
  1338.     }
  1339.     columns [numcols] = p;
  1340.     column_ptr [numcols] = p - original;
  1341.     while (*p && *p != ' ' && *p != '\r' && *p != '\n')
  1342.         p++;
  1343.     }
  1344.     return numcols;
  1345. }
  1346.  
  1347. static int is_num (int idx)
  1348. {
  1349.     if (columns [idx][0] < '0' || columns [idx][0] > '9')
  1350.     return 0;
  1351.     return 1;
  1352. }
  1353.  
  1354. #define free_and_return(x) {free (p_copy); return (x); }
  1355. int parse_ls_lga (char *p, struct stat *s, char **filename, char **linkname)
  1356. {
  1357.     int idx, idx2, num_cols, isconc = 0;
  1358.     long l;
  1359.     struct tm tim;
  1360.     int extfs_format_date = 0;
  1361.     int year_supplied = 0;
  1362.     char *p_copy;
  1363.     
  1364.     s->st_mode = 0;
  1365.     if (strncmp (p, "total", 5) == 0){
  1366.         return 0;
  1367.     }
  1368.     switch (*(p++)) {
  1369.         case 'd': s->st_mode |= S_IFDIR; break;
  1370.         case 'b': s->st_mode |= S_IFBLK; break;
  1371.         case 'c': s->st_mode |= S_IFCHR; break;
  1372.         case 'm': s->st_mode |= S_IFREG; break; /* Don't know what it is :-) */
  1373.         case 'n': s->st_mode |= S_IFREG; break; /* and this as well */
  1374.         case 'l': s->st_mode |= S_IFLNK; break;
  1375. #ifdef IS_IFSOCK        
  1376.         case 's': s->st_mode |= S_IFSOCK; break;
  1377. #endif
  1378.         case 'p': s->st_mode |= S_IFIFO; break;
  1379.         case '-': s->st_mode |= S_IFREG; break;
  1380.         case '?': s->st_mode |= S_IFREG; break;
  1381.         default: return 0;
  1382.     }
  1383.     if (*p == '[') {
  1384.     if (strlen (p) <= 8 || p [8] != ']')
  1385.         return 0;
  1386.     /* Should parse here the Notwell permissions :) */
  1387.     if (S_ISDIR (s->st_mode))
  1388.         s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
  1389.     else
  1390.         s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
  1391.     p += 9;
  1392.     } else {
  1393.     switch (*(p++)) {
  1394.      case 'r': s->st_mode |= 0400; break;
  1395.      case '-': break;
  1396.      default: return 0;
  1397.     }
  1398.     switch (*(p++)) {
  1399.      case 'w': s->st_mode |= 0200; break;
  1400.      case '-': break;
  1401.      default: return 0;
  1402.     }
  1403.     switch (*(p++)) {
  1404.      case 'x': s->st_mode |= 0100; break;
  1405.      case 's': s->st_mode |= 0100 | S_ISUID; break;
  1406.      case 'S': s->st_mode |= S_ISUID; break;
  1407.      case '-': break;
  1408.      default: return 0;
  1409.     }
  1410.     switch (*(p++)) {
  1411.      case 'r': s->st_mode |= 0040; break;
  1412.      case '-': break;
  1413.      default: return 0;
  1414.     }
  1415.     switch (*(p++)) {
  1416.      case 'w': s->st_mode |= 0020; break;
  1417.      case '-': break;
  1418.      default: return 0;
  1419.     }
  1420.     switch (*(p++)) {
  1421.      case 'x': s->st_mode |= 0010; break;
  1422.      case 's': s->st_mode |= 0010 | S_ISGID; break;
  1423.      case 'S': s->st_mode |= S_ISGID; break;
  1424.      case '-': break;
  1425.      default: return 0;
  1426.     }
  1427.     switch (*(p++)) {
  1428.      case 'r': s->st_mode |= 0004; break;
  1429.      case '-': break;
  1430.      default: return 0;
  1431.     }
  1432.     switch (*(p++)) {
  1433.      case 'w': s->st_mode |= 0002; break;
  1434.      case '-': break;
  1435.      default: return 0;
  1436.     }
  1437.     switch (*(p++)) {
  1438.      case 'x': s->st_mode |= 0001; break;
  1439.      case 't': s->st_mode |= 0001 | S_ISVTX; break;
  1440.      case 'T': s->st_mode |= S_ISVTX; break;
  1441.      case '-': break;
  1442.      default: return 0;
  1443.     }
  1444.     }
  1445.     
  1446.     p_copy = strdup (p);
  1447.     num_cols = split_text (p);
  1448.  
  1449.     s->st_nlink = atol (columns [0]);
  1450.     if (s->st_nlink <= 0)
  1451.         free_and_return (0);
  1452.  
  1453.     if (!is_num (1))
  1454.     s->st_uid = finduid (columns [1]);
  1455.     else
  1456.         s->st_uid = (uid_t) atol (columns [1]);
  1457.  
  1458.     /* Mhm, the ls -lg did not produce a group field */
  1459.     for (idx = 3; idx <= 5; idx++) 
  1460.         if ((*columns [idx] >= 'A' && *columns [idx] <= 'S' &&
  1461.             strlen (columns[idx]) == 3) || (strlen (columns[idx])==8 &&
  1462.             columns [idx][2] == '-' && columns [idx][5] == '-'))
  1463.             break;
  1464.     if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
  1465.         free_and_return (0);
  1466.     if (idx < 5) {
  1467.         char *p = strchr(columns [idx - 1], ',');
  1468.         if (p && p[1] >= '0' && p[1] <= '9')
  1469.             isconc = 1;
  1470.     }
  1471.     if (idx == 3 || (idx == 4 && !isconc && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
  1472.         idx2 = 2;
  1473.     else {
  1474.     if (is_num (2))
  1475.         s->st_gid = (gid_t) atol (columns [2]);
  1476.     else
  1477.         s->st_gid = findgid (columns [2]);
  1478.     idx2 = 3;
  1479.     }
  1480.  
  1481.     if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)) {
  1482.         char *p;
  1483.     if (!is_num (idx2))
  1484.         free_and_return (0);
  1485. #ifdef HAVE_ST_RDEV
  1486.     s->st_rdev = (atol (columns [idx2]) & 0xff) << 8;
  1487. #endif
  1488.     if (isconc) {
  1489.         p = strchr (columns [idx2], ',');
  1490.         if (!p || p [1] < '0' || p [1] > '9')
  1491.             free_and_return (0);
  1492.         p++;
  1493.     } else {
  1494.         p = columns [idx2 + 1];
  1495.         if (!is_num (idx2+1))
  1496.             free_and_return (0);
  1497.     }
  1498.     
  1499. #ifdef HAVE_ST_RDEV
  1500.     s->st_rdev |= (atol (p) & 0xff);
  1501. #endif
  1502.     s->st_size = 0;
  1503.     } else {
  1504.     if (!is_num (idx2))
  1505.         free_and_return (0);
  1506.     
  1507.     s->st_size = (size_t) atol (columns [idx2]);
  1508. #ifdef HAVE_ST_RDEV
  1509.     s->st_rdev = 0;
  1510. #endif
  1511.     }
  1512.     
  1513.     p = columns [idx++];
  1514.     
  1515.     if (!strcmp (p, "Jan"))
  1516.         tim.tm_mon = 0; 
  1517.     else if (!strcmp (p, "Feb"))
  1518.         tim.tm_mon = 1; 
  1519.     else if (!strcmp (p, "Mar"))
  1520.         tim.tm_mon = 2; 
  1521.     else if (!strcmp (p, "Apr"))
  1522.         tim.tm_mon = 3; 
  1523.     else if (!strcmp (p, "May"))
  1524.         tim.tm_mon = 4; 
  1525.     else if (!strcmp (p, "Jun"))
  1526.         tim.tm_mon = 5; 
  1527.     else if (!strcmp (p, "Jul"))
  1528.         tim.tm_mon = 6; 
  1529.     else if (!strcmp (p, "Aug"))
  1530.         tim.tm_mon = 7; 
  1531.     else if (!strcmp (p, "Sep"))
  1532.         tim.tm_mon = 8; 
  1533.     else if (!strcmp (p, "Oct"))
  1534.         tim.tm_mon = 9; 
  1535.     else if (!strcmp (p, "Nov"))
  1536.         tim.tm_mon = 10; 
  1537.     else if (!strcmp (p, "Dec"))
  1538.         tim.tm_mon = 11; 
  1539.     else {
  1540.         /* This case should not normaly happen, but in extfs we allow these
  1541.            date formats:
  1542.            Mon DD hh:mm
  1543.            Mon DD YYYY
  1544.            Mon DD YYYY hh:mm
  1545.            MM-DD-YY hh:mm
  1546.            where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
  1547.            YYYY four digit year, hh, mm two digit hour and minute. */
  1548.         if (strlen (p) == 8 && p [2] == '-' && p [5] == '-') {
  1549.             p [2] = 0;
  1550.             p [5] = 0;
  1551.             tim.tm_mon = (int) atol (p);
  1552.             if (!tim.tm_mon)
  1553.                 free_and_return (0)
  1554.             else
  1555.                 tim.tm_mon--;
  1556.             tim.tm_mday = (int) atol (p + 3);
  1557.             tim.tm_year = (int) atol (p + 6);
  1558.             if (tim.tm_year < 70)
  1559.                 tim.tm_year += 70;
  1560.             extfs_format_date = 1;
  1561.         } else
  1562.             free_and_return (0);
  1563.     }
  1564.  
  1565.     if (!extfs_format_date) {
  1566.         if (!is_num (idx))
  1567.         free_and_return (0);
  1568.         tim.tm_mday = (int)atol (columns [idx++]);
  1569.     }
  1570.     
  1571.     if (columns [idx][2] != ':'){
  1572.     /* There is a year */
  1573.         l = atol (columns [idx++]);
  1574.         if (l < 1900 || l > 3000)
  1575.             free_and_return (0);
  1576.         tim.tm_year = (int) (l - 1900);
  1577.         tim.tm_hour = 0;
  1578.         tim.tm_min = 0;
  1579.         tim.tm_sec = 0;
  1580.         year_supplied = 1;
  1581.     }
  1582.     if (columns [idx][2] == ':'){
  1583.         if (sscanf (columns [idx],
  1584.             "%2d:%2d", &tim.tm_hour, &tim.tm_min) != 2) {
  1585.         if (!year_supplied)
  1586.             free_and_return (0);
  1587.         tim.tm_hour = 0;
  1588.         tim.tm_min = 0;
  1589.         tim.tm_sec = 0;
  1590.         } else {
  1591.             idx++;
  1592.             tim.tm_sec = 0;
  1593.             if (!extfs_format_date && !year_supplied) {
  1594.                 tim.tm_year = current_year;
  1595.                 if (tim.tm_mon > current_mon)
  1596.                     tim.tm_year--;
  1597.             }
  1598.         }
  1599.     } else if (!year_supplied)
  1600.         free_and_return (0);
  1601.     tim.tm_isdst = 0;
  1602.     s->st_mtime = mktime (&tim);
  1603.     if (s->st_mtime == -1)
  1604.         s->st_mtime = 0;
  1605.     s->st_atime = s->st_mtime;
  1606.     s->st_ctime = s->st_mtime;
  1607.     s->st_dev = 0;
  1608.     s->st_ino = 0;
  1609. #ifdef HAVE_ST_BLKSIZE
  1610.     s->st_blksize = 512;
  1611. #endif
  1612. #ifdef HAVE_ST_BLOCKS
  1613.     s->st_blocks = (s->st_size + 511) / 512;
  1614. #endif
  1615.     
  1616.     if (((S_ISLNK (s->st_mode) || 
  1617.         (numcols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
  1618.         && (!strcmp (columns [idx+1], "->"))){
  1619.     if (filename)
  1620.         *filename = strdup (columns [idx]);
  1621.     if (linkname)
  1622.         *linkname = strdup (columns [idx+2]);
  1623.     idx += 2;
  1624.     } else {
  1625.     /* Extract the filename from the string copy, not from the columns
  1626.      * this way we have a chance of entering hidden directories like ". ."
  1627.      */
  1628.     if (filename){
  1629.         /* 
  1630.         *filename = strdup (columns [idx++]);
  1631.         */
  1632.         int p;
  1633.         char *s;
  1634.         
  1635.         s = strdup (p_copy + column_ptr [idx++]);
  1636.         p = strlen (s);
  1637.         if (s [p-1] == '\r' || s [p-1] == '\n')
  1638.         s [p-1] = 0;
  1639.         if (s [p-2] == '\r' || s [p-2] == '\n')
  1640.         s [p-2] = 0;
  1641.         
  1642.         *filename = s;
  1643.     }
  1644.     if (linkname)
  1645.         *linkname = NULL;
  1646.     }
  1647.     free_and_return (1);
  1648. }
  1649.